/*
* ============================================================================
*  Name        : sea_engine.cpp
*  Part of     : Finite state machine framework 
*  Description : 
*  Version     : %version: %
*  
*
* ============================================================================
*/

#include "sea_engine.h"
#include "sea_manager.h"
#include "sea_iterator.h"
#include "sea_tree.h"
#include "sea_timer.h"

/**
 * @file sea_engine.cpp
 *
 * @ingroup StateMachineFramework
 */
using namespace sea;

// -------------------------------------------------------------------------------------------------

Engine* Engine::m_engine = NULL;
OsMutex Engine::m_create_lock;

// -------------------------------------------------------------------------------------------------
// Engine ctor
// -------------------------------------------------------------------------------------------------
Engine::Engine( )
{
}

// -------------------------------------------------------------------------------------------------
// Get static engine
// -------------------------------------------------------------------------------------------------
sea::ref< Engine > Engine::engine( )
{
    if ( m_engine == NULL )
    {
        OsMutex::Scoped safe( &m_create_lock );
        if ( m_engine == NULL )
        {
            Engine* newborn = NEW Engine;

            // Start timer server
            newborn->m_timerServer.start( );

            // Instantiate the engine
            m_engine = newborn;
        }
    }
    return m_engine;
}

// -------------------------------------------------------------------------------------------------
// Dtor static engine
// -------------------------------------------------------------------------------------------------
Engine::~Engine( )
{
    OsMutex::Scoped safe( &m_create_lock );
    m_engine = NULL;
}

// -------------------------------------------------------------------------------------------------
// Create manager
// -------------------------------------------------------------------------------------------------
sea::ref< Manager > Engine::create_manager( bool sync_op, bool parser /* = false */ )
{
    sea::ref< Manager > manager;
    manager << NEW Manager;

    if ( manager == NULL )
    {
        return NULL;
    }

    {
        OsMutex::Scoped scoped( &m_lock );
        manager->attach_right_of( this, EngineManager );
    }

    // Enable/disable parser mode
    // Parser mode skips waiting on message
    // Semaphore in the event dispatcher
    manager->set_parser_mode( parser );

    if ( !sync_op )
    {
        // Launch manager's thread
        manager->start( );
    }

    return manager;
}

// -------------------------------------------------------------------------------------------------
// Unrelate manager and the engine
// -------------------------------------------------------------------------------------------------
void Engine::remove_manager( Manager* manager )
{
    ASSERT_DEBUG( manager );

    OsMutex::Scoped safeE( &m_lock );
    OsMutex::Scoped safeM( &manager->m_lock );

    manager->detach( EngineManager );
}

// -------------------------------------------------------------------------------------------------
// Terminate state machine engine all together
// -------------------------------------------------------------------------------------------------
void Engine::terminate( )
{
    if ( !m_engine )
    {
        MetaSystem::destroy( );
        SEA_MEM_LEAKS( );
        return;
    }

    Queue managers;
    Queue statemachines;

    // Stop timer engine
    m_engine->timer_server( )->terminate( );

    {
        OsMutex::Scoped safe( &m_engine->m_lock );

        // Wait all managers to finish
        for( VIterator< Manager > imanager( m_engine, EngineManager ); *imanager; imanager++ ) 
        {
            // Collector for retired managers
            managers.push( *imanager );

            OsMutex::Scoped scoped( imanager->lock( ) );

            // Broadcast terminate message to all state machines
            for( VIterator< StateMachine > ism( *imanager, ManagerStateMachine ); *ism; ism++ ) 
            {
                statemachines.push( *ism );
            }
        }
    }

    // Braodcast Ev_Sys_Terminate message to all state machines
    sea::ref< StateMachine > sm;
    for( ;; )
    {
        sm = statemachines.pop( ).get( );
        if ( sm == NULL )
        {
            break;
        }
        sm->push_event( StateMachine::system( )->Ev_Sys_Terminate( ) );
    }

    // Wait on completion of all managers
    sea::ref< Manager > manager;
    for( ;; )
    {
        manager = managers.pop( ).get( );
        if ( manager == NULL )
        {
            break;
        }
        manager->wait( );
    }

    // Destroy Engine->Managers->StateMachines relationships
    Tree the_tree;
    the_tree.detach_all_branches( m_engine );

    // Final reference
    m_engine->release_ref( );
    m_engine = NULL;

    // Wipe out meta system
    MetaSystem::destroy( );

    SEA_MEM_LEAKS( );
}

// -------------------------------------------------------------------------------------------------
